// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:generate bundle-o=h2_bundle。go-prefix=http2-tags=！nethttpomithttp2 golang。org/x/net/http2 

package http

import (
	"io"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"

	"golang.org/x/net/http/httpguts"
)

// comparable是一种零宽度、不可比较的类型。将其添加到结构
// 也会使该结构不可比较，并且通常不会添加任何大小的
// 结构（只要它是第一个）。
type incomparable [0]func()

// maxInt64是服务器和
// 传输的字节限制读卡器的有效“无限”值。
const maxInt64 = 1<<63 - 1

// aLongTimeAgo是一个非零时间，远在过去，用于
// 立即取消网络操作。
var aLongTimeAgo = time.Unix(1, 0)

// omitBundledHTTP2由omithttp2设置。设置nethttpomithttp2 
// build标记后转到。这意味着h2_捆绑。go不是在中编译的，我们不应该尝试使用它。
var omitBundledHTTP2 bool

// TODO（布拉德菲茨）：把普通的东西移到这里。其他文件在随机位置累积了
// 通用http内容。

// contextKey是用于上下文的值。有价值。它被用作
// 一个指针，因此它适合于接口{}，而无需分配。
type contextKey struct {
	name string
}

func (k *contextKey) String() string { return "net/http context value " + k.name }

// 给定格式为“主机”、“主机：端口”或“[ipv6:：address]：端口”的字符串，
// 如果字符串包含端口，则返回true。
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }

// 根据RFC 3986第6.2.3节的规定，removeEmptyPort将空端口从“：port”中剥离至“.”
// 中。
func removeEmptyPort(host string) string {
	if hasPort(host) {
		return strings.TrimSuffix(host, ":")
	}
	return host
}

func isNotToken(r rune) bool {
	return !httpguts.IsTokenRune(r)
}

// stringContainsCTLByte报告s是否包含任何ASCII控制字符。
func stringContainsCTLByte(s string) bool {
	for i := 0; i < len(s); i++ {
		b := s[i]
		if b < ' ' || b == 0x7f {
			return true
		}
	}
	return false
}

func hexEscapeNonASCII(s string) string {
	newLen := 0
	for i := 0; i < len(s); i++ {
		if s[i] >= utf8.RuneSelf {
			newLen += 3
		} else {
			newLen++
		}
	}
	if newLen == len(s) {
		return s
	}
	b := make([]byte, 0, newLen)
	for i := 0; i < len(s); i++ {
		if s[i] >= utf8.RuneSelf {
			b = append(b, '%')
			b = strconv.AppendInt(b, int64(s[i]), 16)
		} else {
			b = append(b, s[i])
		}
	}
	return string(b)
}

// 没有人是io。没有字节的ReadCloser。Read总是返回EOF 
// Close总是返回nil。它可以在传出的客户端
// 请求中使用，以明确表示一个请求的字节数为零。
// 然而，另一种方法是简单地设置请求。身体归零。
var NoBody = noBody{}

type noBody struct{}

func (noBody) Read([]byte) (int, error)         { return 0, io.EOF }
func (noBody) Close() error                     { return nil }
func (noBody) WriteTo(io.Writer) (int64, error) { return 0, nil }

var (
	// 验证io。无人复制不需要缓冲区：
	_ io.WriterTo   = NoBody
	_ io.ReadCloser = NoBody
)

// PushOptions描述了Pusher的选项。推
type PushOptions struct {
	// Method为承诺的请求指定HTTP方法。
	// 如果设置，则必须是“GET”或“HEAD”。空的意思是“得到”。
	Method string

	// Header指定其他承诺的请求头。这不能包含HTTP/2伪头字段，如“：path”和“：scheme”，
	// 将自动添加。
	Header Header
}

// Pusher是由支持
// HTTP/2服务器推送的响应编写器实现的接口。有关更多背景信息，请参见
// tools.ietf.org/html/rfc7540#section-8.2. 
type Pusher interface {
	// 推送启动HTTP/2服务器推送。这将使用给定的目标和选项构造一个合成的
	// 请求，将该请求序列化为一个PUSH_PROMISE框架，然后使用
	// 服务器的请求处理程序分派该请求。如果opts为nil，则使用默认选项。
	// 
	// 目标必须是绝对路径（如“/path”）或绝对
	// URL，其中包含有效主机和与父请求相同的方案。
	// 如果目标是路径，它将继承
	// 父请求的方案和主机。
	// 
	// HTTP/2规范不允许递归推送和跨权限推送。
	// 推送可能检测到也可能检测不到这些无效推送；但是，合格的客户端将检测并取消无效的
	// 推送。
	// 
	// 希望推送URL X的处理程序应该在发送任何可能触发URL X请求的
	// 数据之前调用push。这避免了
	// 客户端在收到X的push_承诺之前对X发出请求的竞争。
	// 
	// push将在一个单独的GOROUTE中运行到达日期
	// 不确定。任何需要的同步都需要调用方实现
	// 。
	// 
	// 如果客户端已禁用推送，或者基础连接不支持推送，则推送返回ErrNotSupported。
	Push(target string, opts *PushOptions) error
}
